package com.runa.monitor.platinterface.netservice.equipment;

import com.alibaba.fastjson.JSON;
import com.runa.monitor.comm.dto.Archive;
import com.runa.monitor.comm.dto.Meter;
import com.runa.monitor.comm.dto.Node;
import com.runa.monitor.comm.dto.newComm.ValveVO;
import com.runa.monitor.platinterface.backwork.function.TaskFunction;
import com.runa.monitor.platinterface.backwork.impl.RccTaskFunction;
import com.runa.monitor.platinterface.netservice.connect.commstatus.CommStatus;
import com.runa.persistence.config.CommConfig;
import com.runa.persistence.util.LogUtil;
import com.runa.protocol.dto.DtuHDRequestData;
import com.runa.protocol.util.ByteUtil;
import com.runa.rccprotocol.dto.ConcentratorRequestDataProxy;
import com.runa.rccprotocol.dto.ConcentratorResponseData;
import com.runa.rccprotocol.dto.equdata.EquData;
import com.runa.rccprotocol.dto.param.ExtendParam;
import com.runa.rccprotocol.dto.runa.RunaDeviceRequestData;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author zhuj
 */
@Slf4j
public class RunaNativeDriver extends NetEqu<String> {


    private static final byte[] tianGangFunctionCode = new byte[]{0x1f, (byte) 0x90};
    private static final byte[] tianGangFactoryCode = new byte[]{0, 0x11, 0x11};
    private static final byte[] wanHuaFunctionCode = new byte[]{(byte) 0xC3, 0x52};
    private static final byte[] wanHuaFactoryCode = new byte[]{0x01, 0, 0};
    private static final byte equCode = 0x20;

    private final Map<Integer, String> valveIdxSerial = new HashMap<>();
    private final ExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public RunaNativeDriver(String serial, Integer timeout) {
        super(serial, timeout);
    }

    private int lastCollectorPort;

    public static void main(String[] args) {
        RunaDeviceRequestData requestData = new RunaDeviceRequestData();
        requestData.setHouseValveOpenLimit("23305191", 50, 80);
        ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy("14180248",
                new DtuHDRequestData("14180248", requestData));
        System.out.println(ByteUtil.byteToHexString(dataProxy.getData()));
    }

    private void readMeter(Meter meter, TaskFunction function) {
        try {
            LogUtil.info(serial(), "抄表: {}", JSON.toJSONString(meter));
            RunaDeviceRequestData requestData = new RunaDeviceRequestData(serial());
            String model = meter.getModel();
            if ("天罡TG户表".equals(model) || "太原".equals(CommConfig.getProjectAddress())) {
                if ("HS360B".equals(meter.getCollectorModel())) {
                    readCollectorMeter(meter, function);
                    return;
                }
                requestData.readHeatMeter(meter.getSerial(), equCode, tianGangFactoryCode, tianGangFunctionCode);
            } else if ("默认型号".equals(model)) { //天津万华
                requestData.readHeatMeter(meter.getSerial(), equCode, wanHuaFactoryCode, wanHuaFunctionCode);
            } else if (model.startsWith("太原")) {
                requestData.readTyHeatMeter(meter.getSerial(), equCode, tianGangFactoryCode, tianGangFunctionCode);
            } else {
                requestData.readHeatMeter(meter.getSerial());
            }
            DtuHDRequestData dtuHDRequestData = new DtuHDRequestData(serial(), requestData);
            currNode = meter;
            log.debug("瑞纳抄表: {} {}", serial(), ByteUtil.byteToHexString(dtuHDRequestData.getData()));
            ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                    new DtuHDRequestData(this.serial(), requestData));
            dataProxy.setRemain(Collections.singletonList(meter));
            sendRequest(dataProxy, function);
        } catch (Exception e) {
            LogUtil.info(serial(), "异常: {} {}", JSON.toJSONString(meter), e);
        }
    }

    private void readCollectorMeter(Node meter, TaskFunction function) {
        executor.execute(() -> {
            RunaDeviceRequestData requestData = new RunaDeviceRequestData(serial());
            ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(), new DtuHDRequestData(this.serial(), requestData));
            log.debug("多通路采集器: {} {}", meter.getCollector(), meter.getPort());
            if (!(lastCollectorPort == meter.getPort())) {
                requestData.openCollector(meter.getCollector(), meter.getPort());
                RccTaskFunction.SyncFunction chooseFunc = new RccTaskFunction.SyncFunction(function.getPriority());
                sendRequest(dataProxy, chooseFunc);
                chooseFunc.waitResult();
                lastCollectorPort = meter.getPort();
            }
            requestData.readHeatMeter(meter.getSerial(), equCode, tianGangFactoryCode, tianGangFunctionCode);
            DtuHDRequestData dtuHDRequestData = new DtuHDRequestData(serial(), requestData);
            log.debug("瑞纳抄表: {} {}", serial(), ByteUtil.byteToHexString(dtuHDRequestData.getData()));
            dataProxy = new ConcentratorRequestDataProxy(serial(), new DtuHDRequestData(this.serial(), requestData));
            dataProxy.setRemain(Collections.singletonList(meter));
            RccTaskFunction.SyncFunction syncFunction = new RccTaskFunction.SyncFunction(function, timeout(), function.getPriority());
            sendRequest(dataProxy, syncFunction);
            syncFunction.waitResult();
        });
    }

    @Override
    public void readNodeEqu(List<Node> nodeList, TaskFunction function, EquData.EquDataType type) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, nodeList);
            return;
        }
//        List<Node> nodes = filterNodes(nodeSerial, function, type);
        switch (type) {
            case METER:
                readHouseMeter(nodeList, function);
                break;
            case VALVE:
                readHouseValve(nodeList, function);
                break;
            case PUBLIC_UNIT:
                readBuildValve(nodeList, function);
                break;
            default:
        }
    }

    private void readBuildValve(List<Node> nodes, TaskFunction function) {
        RunaDeviceRequestData requestData;
        for (Node node : nodes) {
            try {
                LogUtil.info(serial(), "抄楼阀: {}", JSON.toJSONString(node));
                if (node != null) {
                    int salve;
                    String number = node.getSerial();
                    int length = number.length();
                    try {
                        salve = length > 2 ? Integer.parseInt(number.substring(length - 3)) : Integer.parseInt(number);
                    } catch (Exception e) {
                        salve = 0;
                    }
                    valveIdxSerial.put(salve, node.getSerial());
                    requestData = new RunaDeviceRequestData(serial());
                    requestData.readBuildValve(salve);
                    DtuHDRequestData dtuHDRequestData = new DtuHDRequestData(serial(), requestData);
                    currNode = node;
                    log.debug("瑞纳抄楼阀: {} {}", serial(), ByteUtil.byteToHexString(dtuHDRequestData.getData()));
                    ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                            new DtuHDRequestData(this.serial(), requestData));
                    dataProxy.setRemain(Collections.singletonList(node));
                    sendRequest(dataProxy, function);
                }
            } catch (Exception e) {
                LogUtil.info(serial(), "异常: {} {}", JSON.toJSONString(node), e);
            }
        }
    }


    private void readHouseValve(List<Node> nodes, TaskFunction function) {
        RunaDeviceRequestData requestData;
        for (Node node : nodes) {
            try {
                LogUtil.info(serial(), "抄户阀: {}", JSON.toJSONString(node));
                requestData = new RunaDeviceRequestData(serial());
                requestData.readHouseValve(node.getSerial());
                DtuHDRequestData dtuHDRequestData = new DtuHDRequestData(serial(), requestData);
                currNode = node;
                log.debug("瑞纳抄户阀: {} {}", serial(), ByteUtil.byteToHexString(dtuHDRequestData.getData()));
                ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                        new DtuHDRequestData(this.serial(), requestData));
                dataProxy.setRemain(Collections.singletonList(node));
                sendRequest(dataProxy, function);
            } catch (Exception e) {
                LogUtil.info(serial(), "异常: {} {}", JSON.toJSONString(node), e);
            }
        }
    }

    private void readHouseMeter(List<Node> nodes, TaskFunction function) {
        List<Meter> meters = nodes.stream().map(node -> (Meter) node)
                .sorted(Comparator.comparing(Node::getPort))
                .collect(Collectors.toList());
        for (Meter meter : meters) {
            this.readMeter(meter, function);
        }
    }

    @Override
    public void receive(ConcentratorResponseData responseData) {
        if (responseData.getData() instanceof List) {
            List list = (List) responseData.getData();
            if (!list.isEmpty() && list.get(0) instanceof ValveVO) {
                ValveVO valveVO = (ValveVO) list.get(0);
                String serial = valveIdxSerial.get(valveVO.getIndex());
                if (serial != null) valveVO.setValveSerial(serial);
            }
        }
        super.receive(responseData);
    }

    @Override
    public void setNodeEquArchive(List<Archive> archives, TaskFunction function) {

    }

    @Override
    public void clearNodeEquArchive(TaskFunction function) {

    }

    @Override
    public void valveOnOffControl(List<Node> valves, boolean ifLock, Integer opening, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        RunaDeviceRequestData requestData;
        for (Node valve : valves) {
//            Valve valve = findValve(vSerial);
            if (valve != null) {
                requestData = new RunaDeviceRequestData();
                requestData.setHouseValveOpen(valve.getSerial(), opening, ifLock);
                ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                        new DtuHDRequestData(this.serial(), requestData));
                dataProxy.setRemain(valve);
                sendRequest(dataProxy, function);
            }
        }
    }

    public void valveTemperatureLock(List<Node> valves, Boolean ifLock, Float temperature, TaskFunction function) {
        valveTemperatureControl(valves, ifLock, temperature, null, null, function);
    }

    public void valveTemperatureLimit(List<Node> valves, Float up, Float down, TaskFunction function) {
        valveTemperatureControl(valves, null, null, up, down, function);
    }

    public void valveTemperatureControl(List<Node> valves, Boolean ifLock,
                                        Float temperature, Float up, Float down, TaskFunction<Object, Object, String> function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        RunaDeviceRequestData requestData;
        for (Node valve : valves) {
            if (valve != null) {
                requestData = new RunaDeviceRequestData();
                requestData.setHouseValveTemp(valve.getSerial(), temperature, ifLock, up, down);
                ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                        new DtuHDRequestData(this.serial(), requestData));
                dataProxy.setRemain(valve);
                sendRequest(dataProxy, function);
            }
        }
    }

    @Override
    public void readNodeRecord(List<Integer> indexes, TaskFunction function) {

    }

    @Override
    public void sentPublicUnitRecord(List<Node> valves, Integer model, Map param, TaskFunction function) {
        RunaDeviceRequestData requestData;
        for (Node unit : valves) {
            if (unit != null) {
                int id;
                String number = unit.getSerial();
                int length = number.length();
                try {
                    id = length > 2 ? Integer.parseInt(number.substring(length - 3)) : Integer.parseInt(number);
                } catch (Exception e) {
                    id = 0;
                }
                switch (model) {
                    case 1:
                        requestData = new RunaDeviceRequestData();
                        requestData.setBuildValveOpen(id, (int) Float.parseFloat(param.get("targetValue").toString()));
                        ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                                new DtuHDRequestData(this.serial(), requestData));
                        dataProxy.setRemain(unit);
                        sendRequest(dataProxy, function);
                        break;
                    case 2:
                        break;
                    case 3:
                        break;
                    case 4:
                        Map<String, Integer> valveUpDown = (Map<String, Integer>) param.get("valveUpDown");
                        requestData = new RunaDeviceRequestData();
                        requestData.setBuildValveLimit(id, valveUpDown.get("valveUp"), valveUpDown.get("valveDown"));
                        dataProxy = new ConcentratorRequestDataProxy(serial(),
                                new DtuHDRequestData(this.serial(), requestData));
                        dataProxy.setRemain(unit);
                        sendRequest(dataProxy, function);
                        break;
                    default:
                }
            }
        }
    }


    @Override
    public void sentPublicUnitModel(List<Node> valves, boolean model, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        for (Node valve : valves) {
            int id;
            String number = valve.getSerial();
            int length = number.length();
            try {
                id = length > 2 ? Integer.parseInt(number.substring(length - 3)) : Integer.parseInt(number);
            } catch (Exception e) {
                id = 0;
            }
            RunaDeviceRequestData requestData = new RunaDeviceRequestData();
            requestData.setBuildValveModel(id, model);
            ConcentratorRequestDataProxy dataProxy = new ConcentratorRequestDataProxy(serial(),
                    new DtuHDRequestData(this.serial(), requestData));
            dataProxy.setRemain(valve);
            sendRequest(dataProxy, function);
        }
    }

    @Override
    public void sentExtendParam(ExtendParam param, TaskFunction function) {

    }

    @Override
    public void readPublicUnitRecord(Node valveSerial, Integer model, TaskFunction function) {

    }

    @Override
    public void readPublicUnitAndMeter(Node valveSerial, Node meterSerial, TaskFunction function) {

    }

    @Override
    public void heartbeatUpdate() {

    }

    @Override
    public boolean heartStop() {
        return false;
    }

}
